package com.kuaishou.locallife.open.api.common.utils;

import static com.google.gson.reflect.TypeToken.getParameterized;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.kuaishou.locallife.open.api.common.utils.adapter.CustomizedTypeAdapter;
import com.kuaishou.locallife.open.api.common.utils.adapter.IntTypeAdapter;
import com.kuaishou.locallife.open.api.common.utils.adapter.LongTypeAdapter;

/**
 * json工具类
 *
 * @author shuxiaohui <shuxiaohui@kuaishou.com>
 * Created on 2020-03-31
 */
public class GsonUtils {

    private static final String EMPTY_JSON = "{}";
    private static final String EMPTY_ARRAY_JSON = "[]";
    private static final Gson GSON;

    static {
        TypeAdapter customizedAdapter = new CustomizedTypeAdapter();
        GSON = new GsonBuilder()
                .registerTypeAdapter(new TypeToken<Map>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<HashMap>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<Map<String, Object>>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<HashMap<String, Object>>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<List>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<ArrayList>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<List<Object>>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(new TypeToken<ArrayList<Object>>() {
                }.getType(), customizedAdapter)
                .registerTypeAdapter(Integer.class, new IntTypeAdapter())
                .registerTypeAdapter(int.class, new IntTypeAdapter())
                .registerTypeAdapter(Long.class, new LongTypeAdapter())
                .registerTypeAdapter(long.class, new LongTypeAdapter())
                .serializeNulls()
                .create();
    }

    public static String toJSON(Object obj) {
        if (obj == null) {
            return null;
        }
        return GSON.toJson(obj);
    }

    public static String toPrettyJson(Object obj) {
        if (obj == null) {
            return null;
        }
        return new GsonBuilder().setPrettyPrinting().create().toJson(obj);
    }

    public static String toEscapeHtmlPrettyJson(Object obj) {
        if (obj == null) {
            return null;
        }
        return new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create().toJson(obj);
    }

    /**
     * @see TypeToken#getParameterized(Type, Type...)
     */
    public static <T> T fromJSON(String json, Type type) {
        if (json == null) {
            return null;
        }
        return GSON.fromJson(json, type);
    }

    public static <T> T fromJSON(String json, Class<T> valueType) {
        if (json == null) {
            return null;
        }
        return GSON.fromJson(json, valueType);
    }

    public static <E, T extends Collection<E>> T fromJSON(String json,
            Class<? extends Collection> collectionType, Class<E> valueType) {
        if (KsStringUtils.isBlank(json)) {
            json = EMPTY_ARRAY_JSON;
        }
        return GSON.fromJson(json, getParameterized(collectionType, valueType).getType());
    }

    public static <K, V, T extends Map<K, V>> T fromJSON(String json,
            Class<? extends Map> mapType, Class<K> keyType, Class<V> valueType) {
        if (KsStringUtils.isBlank(json)) {
            json = EMPTY_JSON;
        }
        return GSON.fromJson(json, getParameterized(mapType, keyType, valueType).getType());
    }

    public static Map<String, String> fromJson(String string) {
        return fromJSON(string, Map.class, String.class, String.class);
    }

    public static <E> E getValueFromJson(String json, Enum key, Class<E> valueType) {
        return getValueFromJson(json, key.name(), valueType);
    }

    public static <E> E getValueFromJson(String json, String key, Class<E> valueType) {
        Object value = fromJSON(json, Map.class, String.class, Object.class).get(key);
        if (null != value) {
            if (valueType.isInstance(value)) {
                return valueType.cast(value);
            } else if (value instanceof String) {
                return fromJSON((String) value, valueType);
            } else {
                return fromJSON(toJSON(value), valueType);
            }
        }
        return null;
    }
}
